package gu.sql2java.manager;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static gu.sql2java.manager.Managers.baseManagerOf;
import static gu.sql2java.manager.Managers.getBaseTableManager;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.MoreObjects.firstNonNull;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;

import gu.sql2java.BaseBean;
import gu.sql2java.BaseFuzzyMatchFilter;
import gu.sql2java.BaseRow;
import gu.sql2java.IFuzzyMatchFilter;
import gu.sql2java.RowMetaData;
import gu.sql2java.TableListener;
import gu.sql2java.TableManager;
import gu.sql2java.BaseFuzzyMatchFilter.DefaultFuzzyFilter;
import gu.sql2java.IFuzzyMatchFilter.MatchErrorHandler;
import gu.sql2java.TableListener.Adapter;
import gu.sql2java.TableManager.Action;
import gu.sql2java.exception.RuntimeDaoException;
import gu.sql2java.guava.MapBuilder;

/**
 * 对表字段实现模糊搜索的基类
 * @author guyadong
 *
 * @param <B> 数据库表记录类型
 * @param <K> 搜索键类型
 */
public abstract class BaseFieldSearcher <B extends BaseBean,K>{
	protected final RowMetaData metaData;
	protected final BaseTableManager<B> manager;
	private final int[] keyIds;
	private final int[] effectColumnIds;
	/**
	 * 主键--搜索键映射
	 */
	protected final ConcurrentMap<Object[], K> pks;
	protected final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
	private Collection<B> effectedBeans;
	private BaseRow beforeUpdatedBean;
	private final Listener listener;
	private IFuzzyMatchFilter<K> defaultMatchFilter;
	private MatchErrorHandler<K> errorHandler;
	public BaseFieldSearcher(RowMetaData metaData,int ...effectColumnIds) {
    	this.metaData = checkNotNull(metaData,"metaData is null");
		this.manager = getBaseTableManager(metaData.tablename);
		this.keyIds = metaData.primaryKeyIds;
		this.effectColumnIds = checkEffectIds(effectColumnIds);
		this.pks = MapBuilder.newConcurrentMap();
		this.listener = new Listener();
		this.defaultMatchFilter = new BaseFuzzyMatchFilter.DefaultFuzzyFilter<>();
	}
	public BaseFieldSearcher(RowMetaData metaData,String ...effectColumnNames) {
		this(metaData, checkNotNull(metaData,"metaData is null").columnIDsOf(effectColumnNames));
	}
	public <M extends TableManager<B>>BaseFieldSearcher(Class<M>interfaceClass,int[] effectColumnId) {
		this(baseManagerOf(interfaceClass).metaData, effectColumnId);
	}
	public <M extends TableManager<B>>BaseFieldSearcher(Class<M>interfaceClass,String ... effectColumnNames) {
		this(baseManagerOf(interfaceClass).metaData, effectColumnNames);
	}
	
	public String getTablename(){
		return metaData.tablename;
	}
	private int[] checkEffectIds(int[] effectColumnIds){
		checkArgument(effectColumnIds != null && effectColumnIds.length > 0,"effectColumnIds is null or empty");
		for(int columnId:checkNotNull(effectColumnIds,"effectColumnIds is null")){
			checkArgument(columnId>=0 && columnId<metaData.columnCount,"INVALID columnId %s",columnId);
		}
		return effectColumnIds;
	}
	
	/**
	 * 根据bean记录计算出key的值
	 * @param bean
	 * @return K instance
	 */
	protected abstract K keyOf(B bean);
	protected IFuzzyMatchFilter<K> getDefaultMatchFilter(){
		return defaultMatchFilter;
	}
	public BaseFieldSearcher<B, K> setDefaultMatchFilter(IFuzzyMatchFilter<K> defaultMatchFilter) {
		if(defaultMatchFilter != null){
			this.defaultMatchFilter = defaultMatchFilter;
		}
		return this;
	}
	public BaseFieldSearcher<B, K> setErrorHandler(MatchErrorHandler<K> errorHandler) {
		this.errorHandler = errorHandler;
		return this;
	}
	/**
	 * 返回当记录更新时受影响的其他记录,如果没有返回空集合,
	 * 子类可根据需要重写此方法
	 * @param beforeUpdateBean
	 * @return 受影响的记录集合
	 */
	protected Collection<B> getEffectedBeansOnUpdate(B beforeUpdateBean){
		return Collections.emptyList();
	}
	public BaseFieldSearcher<B, K> init(){
		WriteLock lock = rwlock.writeLock();
		lock.lock();
		try{
			pks.clear();
			manager.loadAll(new Action<B>(){
				@Override
				public void call(B bean) {
					add(bean);
				}});
			manager.registerListener(listener);
		} finally {
			lock.unlock();
		}
		return this;
	}
	
	public BaseFieldSearcher<B, K> uninit(){
		WriteLock lock = rwlock.writeLock();
		lock.lock();
		try{
			pks.clear();
			manager.unregisterListener(listener);
		} finally {
			lock.unlock();
		}
		return this;
	}
	protected void add(B bean) {
		if(bean != null){
			K key = keyOf(bean);
			if(key != null){
				Object[] pk = bean.primaryValues();
				pks.put(pk,key);
			}
		}
	}
	protected void update(B bean) {
		if(bean != null){
			K newKey = keyOf(bean);
			Object[] pk = bean.primaryValues();
			if(newKey != null){
				// 增加新的key
				pks.put(pk, newKey);
			}
		
		}
	}
	/**
	 * @param key 请求匹配的key
	 * @param matchFlags 匹配标志
	 * @param matchFilter 模糊匹配过滤器
	 * @param pkFilter 主键过滤器,用于根据主键过滤表记录
	 * @return 返回匹配的记录
	 */
	public final Multimap<K, Object[]> searchPk(K key, int matchFlags,IFuzzyMatchFilter<K> matchFilter, Predicate<Object[]> pkFilter){
		SetMultimap<K, Object[]> mm = MultimapBuilder.hashKeys().hashSetValues().build();
		if(key == null){
			return mm;
		}
		ReadLock lock = rwlock.readLock();
		lock.lock();
		try {
			Map<Object[], K> matched = Maps.filterEntries(pks, new EntryMatchFilter(key,matchFlags,matchFilter, pkFilter));
			for( Entry<Object[], K> entry:matched.entrySet()){
				mm.put(entry.getValue(), entry.getKey());
			}
			return mm;
		} finally {
			lock.unlock();
		}
	}
	private static final Function<Object[],Object> onePk = new Function<Object[],Object>(){

		@Override
		public Object apply(Object[] input) {
			return input[0];
		}
	};
	/**
	 * 单主键模糊匹配
	 * @param key 请求匹配的key
	 * @param matchFlags 匹配标志
	 * @param matchFilter 模糊匹配过滤器
	 * @param pkFilter 主键过滤器,用于根据主键过滤表记录
	 * @return 返回匹配的记录
	 */
	@SuppressWarnings("unchecked")
	public final <T> Multimap<K, T> search(K key, int matchFlags, IFuzzyMatchFilter<K> matchFilter, Predicate<T> pkFilter){
		checkState(keyIds.length == 1,"Unsupported Operation caused by the primary count > 1");
		
		Predicate<Object[]> objsPkFilters = null;
		if(pkFilter != null){
			objsPkFilters = Predicates.compose(pkFilter,  (Function<Object[], T>)onePk);
		}
		Multimap<K, Object[]> pk = searchPk(key, matchFlags, matchFilter, objsPkFilters); 
		return Multimaps.transformValues(pk, (Function<Object[], T>)onePk);
	}
	
	public K getPk(Object[] pk){
		if(pk == null){
			return null;
		}
		ReadLock lock = rwlock.readLock();
		lock.lock();
		try {
			return pks.get(pk);
		} finally {
			lock.unlock();
		}
	}

	public int[] getEffectColumnIds() {
		return effectColumnIds;
	}

	/**
	 * @return first column id of effectColumnIds
	 */
	public int getEffectColumnId() {
		return effectColumnIds[0];
	}
	
	/**
	 * @return first column name of effectColumnIds
	 */
	public String getEffectColumnName() {
		return metaData.columnNameOf(effectColumnIds[0]);
	}
	
	private class Listener extends TableListener.Adapter<B>{
		@Override
		public void afterInsert(B bean) throws RuntimeDaoException {
			WriteLock lock = rwlock.writeLock();
			lock.lock();
			try{
				add(bean);
			} finally {
				lock.unlock();
			}
		}
		private boolean isModified(B bean){
			for(int columnId:effectColumnIds){
				if(bean.isModified(columnId)){
					return true;
				}
			}
			return false;
		}
		@Override
		public void beforeUpdate(B bean) throws RuntimeDaoException {
			// 保留更新前的数据
			beforeUpdatedBean = ((BaseRow)bean).clone();
			if(isModified(bean)){
				// 如果指定的字段被更新保留受影响的数据
				effectedBeans = getEffectedBeansOnUpdate(bean);
			}else{
				effectedBeans = Collections.emptyList();
			}
		}
	
		@Override
		public void afterUpdate(B bean) throws RuntimeDaoException {
			// effectedBeans 为 null，只可能因为侦听器是被异步调用的
			checkState(beforeUpdatedBean != null,"beforeUpdatedBean must not be null");
			WriteLock lock = rwlock.writeLock();
			lock.lock();
			try{
				for(B effectedBean:effectedBeans){
					update(effectedBean);
				}
				update(bean);
			} finally {
				lock.unlock();
				beforeUpdatedBean = null;
			}
		}
	
		@Override
		public void afterDelete(B bean) throws RuntimeDaoException {
			WriteLock lock = rwlock.writeLock();
			lock.lock();
			try {
				Object[] pk = bean.primaryValues();
				pks.remove(pk);
			} finally {
				lock.unlock();
			}
		}
	}
	class EntryMatchFilter implements Predicate<Entry<Object[], K>>{
		private final IFuzzyMatchFilter<K> matchFilter;
		private final Predicate<Object[]> pkFilter;
		
		EntryMatchFilter(K key,int matchFlags,IFuzzyMatchFilter<K> matchFilter, Predicate<Object[]> pkFilter) {
			this.matchFilter = firstNonNull(matchFilter,getDefaultMatchFilter()).withErrorHandler(errorHandler)
					.withPattern(key, matchFlags);
			this.pkFilter = firstNonNull(pkFilter, Predicates.<Object[]>alwaysTrue());
		}
		@Override
		public boolean apply(Entry<Object[], K> input) {
			return matchFilter.apply(input.getValue()) && pkFilter.apply(input.getKey());
		}
		
	}
}
